class_name GameplayAbilityManager

##
var owner: AbilitySystemComponent

## { ability_name: { target: [abilty_specs, ...] } }
var ability_map: Dictionary


func _init(init_owner: AbilitySystemComponent) -> void:
	owner = init_owner


func grant_ability(ability_asset: GameplayAbilityAsset) -> void:
	if not owner.tag_manager.has_all_tags(ability_asset.tags_all_required) or\
	   owner.tag_manager.has_any_tags(ability_asset.tags_cancelled_ability):
		return
	
	# TODO: 重复授予能力
	var ability_cache: Array = ability_map.get(ability_asset.name, [])
	if ability_cache:
		return

	ability_map[ability_asset.name] = [ability_asset.create_spec(owner), {}]


func grant_abilities(ability_assets: Array[GameplayAbilityAsset]) -> void:
	for ability_asset: GameplayAbilityAsset in ability_assets:
		grant_ability(ability_asset)


func remove_ability(ability_asset: GameplayAbilityAsset) -> void:
	var ability_cache: Array = ability_map.get(ability_asset.name)
	if not ability_cache:
		return
	
	var ability_spec_cache = ability_cache[1] as Dictionary

	for ability_specs: Array in ability_spec_cache.values():
		for ability_spec: GameplayAbilitySpec in ability_specs:
			ability_spec.end_ability()

	ability_map.erase(ability_asset.name)


func remove_abilities(ability_assets: Array[GameplayAbilityAsset]) -> void:
	for ability_asset: GameplayAbilityAsset in ability_assets:
		remove_ability(ability_asset)


func activate_ability(ability_asset: GameplayAbilityAsset) -> void:
	var ability_cache: Array = ability_map.get(ability_asset.name)
	if not ability_cache:
		return
	
	var ability = ability_cache[0] as GameplayAbility
	var ability_spec_cache = ability_cache[1] as Dictionary

	if not ability:
		return
	
	ability.active = true
	
	for ability_specs: Array in ability_spec_cache.values():
		for ability_spec: GameplayAbilitySpec in ability_specs:
			ability_spec.activate()


func activate_abilities(ability_assets: Array[GameplayAbilityAsset]) -> void:
	for ability_asset: GameplayAbilityAsset in ability_assets:
		activate_ability(ability_asset)


func deactivate_ability(ability_asset: GameplayAbilityAsset) -> void:
	var ability_cache: Array = ability_map.get(ability_asset.name)
	if not ability_cache:
		return
	
	var ability = ability_cache[0] as GameplayAbility
	var ability_spec_cache = ability_cache[1] as Dictionary

	if not ability:
		return
	
	ability.active = false
	
	for ability_specs: Array in ability_spec_cache.values():
		for ability_spec: GameplayAbilitySpec in ability_specs:
			ability_spec.deactivate()


func deactivate_abilities(ability_assets: Array[GameplayAbilityAsset]) -> void:
	for ability_asset: GameplayAbilityAsset in ability_assets:
		deactivate_ability(ability_asset)


func try_apply_ability(ability_asset: GameplayAbilityAsset, target: AbilitySystemComponent) -> void:
	if owner.tag_manager.has_any_tags(ability_asset.tags_cancelled_ability):
		return

	var ability_cache: Array = ability_map.get(ability_asset.name)
	if not ability_cache:
		return

	var ability = ability_cache[0] as GameplayAbility
	var ability_spec_cache = ability_cache[1] as Dictionary

	if not ability.can_apply():
		return
	
	# TODO: 处理堆叠
	var ability_spec = ability.create_spec(target)
	
	ability_spec_cache.get_or_add(target, []).append(ability_spec)
	
	ability_spec.apply()


func try_apply_ability_by_name(ability_name: String, target: AbilitySystemComponent) -> void:
	if ability_map.has(ability_name):
		try_apply_ability(ability_map[ability_name][0].ability_asset, target)


func try_apply_abilities(ability_assets: Array[GameplayAbilityAsset], target: AbilitySystemComponent) -> void:
	for ability_asset: GameplayAbilityAsset in ability_assets:
		try_apply_ability(ability_asset, target)


func end_ability(ability_asset: GameplayAbilityAsset, target: AbilitySystemComponent) -> void:
	var ability_cache: Array = ability_map.get(ability_asset.name)
	if not ability_cache:
		return

	var ability_spec_cache = ability_cache[1] as Dictionary
	var ability_spec = ability_spec_cache.get(target) as GameplayAbilitySpec

	if ability_spec:
		ability_spec.end_ability()
		ability_spec_cache.erase(target)


func on_update(delta: float) -> void:
	for ability_cache in ability_map.values():
		if not ability_cache:
			continue

		var ability = ability_cache[0] as GameplayAbility
		var ability_spec_cache = ability_cache[1] as Dictionary

		ability.on_update(delta)
		
		var expired_ability_specs: Array = []

		for ability_specs: Array in ability_spec_cache.values():
			for ability_spec: GameplayAbilitySpec in ability_specs:
				if ability_spec.is_expired:
					expired_ability_specs.append(ability_spec)
				else:
					ability_spec.on_update(delta)
		
		for expired_ability_spec: GameplayAbilitySpec in expired_ability_specs:
			ability_spec_cache[expired_ability_spec.target].erase(expired_ability_spec)
